
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Drawing;
using System.Web;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;
using Swaf.Security;
using Swaf.Logging;

using Syrinx;

namespace MediaCache
{
	/// <summary>
	/// Provides a virtual directory of images from a specified location within the AOM.
	/// </summary>
	/// <remarks>The rootName of this class is used as directory name in the HTTP web site
	/// so if the name is "images" and the base URL for the site is YourSite.com, then the 
	/// directory this handler would service would be "http://YourSite.com/images".
	/// <p>To use this class, you must add entries within the Web.config file for this
	/// ASP.NET site this handler will operate in.  The test ASP.NET site for this
	/// handler has the following:<code>
	/// <httpHandlers>
	///   <add verb="*" path="images/*" validate="true" type="MediaCache.ImageRequestHandlerFactory, MediaCache" />
	///   <add verb="*" path="images/*/*" validate="true" type="MediaCache.ImageRequestHandlerFactory, MediaCache" />
	///   <add verb="*" path="images/*/*/*" validate="true" type="MediaCache.ImageRequestHandlerFactory, MediaCache" />
	///   <add verb="*" path="images/*/*/*/*" validate="true" type="MediaCache.ImageRequestHandlerFactory, MediaCache" />
	/// </httpHandlers>
	/// </code>
	/// </p><p>
	/// The path attribute of the add element uses the name images, which corisponds to the 
	/// root name assigned to the handler.  It is important not to have a different value for
	/// the objects fullName property then the first part of the url in the path.
	/// </p><p>
	/// The example httpHandler registration above included the same rootName with deeper '*'
	/// names for each subsequent addition.  This is due to IIS and ASP.NET handlers not allowing
	/// a single registration to handle all directories under a specific name like images.  By having
	/// 4 levels deep under images all registered like they are above, the handler can handle
	/// image caches that have directories within them, up to the number of levels you specify.
	/// </p><p>
	/// </p></remarks>
	public class ImageRequestHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
	{
        /// <summary>
        /// 
        /// </summary>
		protected bool m_useSession = false;
        /// <summary>
        /// 
        /// </summary>
		protected string m_rootName = "images";
        /// <summary>
        /// 
        /// </summary>
		protected string m_catalogName = "images";
        /// <summary>
        /// 
        /// </summary>
		protected string m_urlSearchKey;
        /// <summary>
        /// 
        /// </summary>
		protected string m_baseName;
        /// <summary>
        /// 
        /// </summary>
        protected string m_privName;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="catalogName"></param>
		public ImageRequestHandler(string catalogName)
		{
			m_catalogName = catalogName;
            m_privName = "AppFunctions." + m_catalogName;
			m_urlSearchKey = m_catalogName + "/";
			System.Text.StringBuilder buff = new System.Text.StringBuilder(50);
			buff.Append(m_rootName).Append(".");
			buff.Append(m_catalogName).Append(".");
			m_baseName = buff.ToString();
		}
		
		#region IHttpHandler Members

		/// <summary>
		/// Uses the ImageCache XML Catalog element to get images being requested
		/// from it.  This class exposes a virtually unlimited number of images
		/// in various sizes based on the capabilities of the ImageCache itself.
		/// </summary>
		/// <remarks>
		/// The root map key name is defaulted to "images" and currently has no way
		/// of overiding that.  The "standard" is for image cache definitions to all
		/// be located in the same XML catalog named "images".  The name of the image
		/// cache to use is passed into this handler as a constructor parameter.
		/// <p>By having a constructor parameter, this class cannot be used as a direct
		/// instance in an httpHandler verb from the web.config file.  The 
		/// ImageRequestHandlerFactory class must be used, which uses the first folder
		/// name of the request URL after the base web site name to dictate the catalog and
		/// which ImageRequestHandler to use.
		/// </remarks>
		/// <param name="context"></param>
		public void ProcessRequest(HttpContext context)
		{
			string name = context.Request.FilePath;

			App.Trace.write(TraceLevels.Normal, "<ImageRequest sessionId='{0}' userId='{1}' viewName='{2}' userIP='{3}' userClient='{4}' />",
				App.sessionId, CurrentUser.get("userName"), name, context.Request.UserHostAddress, context.Request.UserAgent);
			
			if (App.securityMgr.currentPrincipal.getPrivilege(m_privName) < Privileges.ReadOnly)
                context.Response.StatusCode = 401;
            else
            {
                string imgName = null;
				try
				{
					MediaStream media = null;
					int foundPos = name.IndexOf(m_urlSearchKey);
					if (foundPos != -1)
					{
						imgName = getFullName(name, foundPos + m_urlSearchKey.Length);
						IResults rc = App.ruleMgr.execute("getImage", imgName);
						if (rc.ExceptionInfo != null)
							throw rc.ExceptionInfo;
						media = (MediaStream)rc.CallResults;
					}

					if (media == null)
					{
						string defName = m_baseName  + ActiveCompany.get("settings.defaultMediaName");
						IResults rc = App.ruleMgr.execute("getImage", defName);
						media = (MediaStream)rc.CallResults;
					}

					if (media != null)
					{
						context.Response.ContentType = media.MimeType;
						context.Response.Cache.SetCacheability(HttpCacheability.Public);
						context.Response.Cache.SetExpires(DateTime.Now.AddYears(1));
						context.Response.Cache.SetMaxAge(new TimeSpan(365, 0, 0, 0));
						context.Response.BinaryWrite(media.Bytes);
						media.Dispose();
					}
					else
						context.Response.StatusCode = 404;
					//context.Response.End();
				}
				catch
				{
					context.Response.StatusCode = 403;
				}
            }
		}
        /// <summary>
        /// 
        /// </summary>
		public bool IsReusable {get{return false;}}
		#endregion
        /// <summary>
        /// 
        /// </summary>
		protected INameAddressSupport m_map
		{
			get{return (m_useSession)?Application.currentApp.sessionData:
										Application.currentApp.appData;}
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="imgName"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
		protected string getFullName(string imgName, int pos)
		{
            System.Text.StringBuilder buff = new System.Text.StringBuilder(250);
			buff.Append(m_baseName);
            buff.Append(imgName, pos, imgName.Length - pos);
			return buff.ToString();
        }

		protected Swaf.IApplication App { get { return Swaf.Application.currentApp; } }
		protected IBizObj CurrentUser { get { return App.securityMgr.currentPrincipal.additionalInfo as IBizObj; } }
		protected Swaf.BGuid.Guid CurrentCompanyId
		{
			get
			{
				Debug.Assert(CurrentUser.get("companyId") != null);
				Debug.Assert(CurrentUser.get("companyId") is Swaf.BGuid.Guid);
				return (Swaf.BGuid.Guid)CurrentUser.get("companyId");
			}
		}

		protected Swaf.BGuid.Guid ActiveCompanyId
		{
			get
			{
				Debug.Assert(CurrentUser.get("activeCompanyId") != null);
				Debug.Assert(CurrentUser.get("activeCompanyId") is Swaf.BGuid.Guid);
				return (Swaf.BGuid.Guid)CurrentUser.get("activeCompanyId");
			}
		}

		protected IBizObj ActiveCompany
		{
			get
			{
				return App.ruleMgr.execute("getCachedCompanyInfo", ActiveCompanyId).CallResults as IBizObj;
			}
		}

	}
}
